package com.ztjava.plugin.weixin;

import java.net.URLEncoder;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;

import com.alibaba.fastjson.JSONObject;
import com.jfinal.kit.HashKit;
import com.jfinal.kit.HttpKit;
import com.jfinal.kit.Ret;
import com.jfinal.plugin.redis.Cache;
import com.jfinal.plugin.redis.Redis;
import com.xiaoleilu.hutool.date.DateUtil;
import com.ztjava.core.Constant;
import com.ztjava.core.tool.UploadTool;
import com.ztjava.core.utiliy.StringUtil;

public class WeixinSdk {
	private static Logger logger = Logger.getLogger(WeixinSdk.class);
	
	private String name;
	private String appid;
	private String secret;
	private String token;
	
	private final static String errcode = "errcode";
	/***
	 * 获取access_token
	 * @return
	 */
	private static final Object lock = new Object();
	
	public WeixinSdk(String name,String appid,String secret,String token){
		this.name = name;
		this.appid = appid;
		this.secret = secret;
		this.token = token;
	}
	
	public String getAccessToken(){
		Cache cache = Redis.use();
		String result = "";
		try{
			Map<String,String> queryParas = new HashMap<String,String>();
			queryParas.put("grant_type", "client_credential");
			queryParas.put("appid", appid);
			queryParas.put("secret", secret);
			result = HttpKit.get("https://api.weixin.qq.com/cgi-bin/token", queryParas);
		}catch (Exception e){
			e.printStackTrace();
			return "";
		}
		JSONObject json = JSONObject.parseObject(result);
		if(json.get(errcode)==null||json.getInteger(errcode)==0){
			String accessToken = json.getString("access_token");
			cache.setex(Constant.R_PRE+"WEIXINSDK:ACCESSTOKEN:"+appid, 7000,accessToken);
			return accessToken;
		}
		logger.error("WeixinSdk的access_token调用失败："+json.toJSONString());
		return "";
	}
	
	public String access_token(){
		Cache cache = Redis.use();
		if(cache.exists(Constant.R_PRE+"WEIXINSDK:ACCESSTOKEN:"+appid)){
			return cache.get(Constant.R_PRE+"WEIXINSDK:ACCESSTOKEN:"+appid);
		}
		String access_token = "";
		synchronized (lock) {
			//也许别人处理好了，自己就不要处理了。
			if(cache.exists(Constant.R_PRE+"WEIXINSDK:ACCESSTOKEN:"+appid)){
				access_token = cache.get(Constant.R_PRE+"WEIXINSDK:ACCESSTOKEN:"+appid);
			}
			access_token = getAccessToken();
		}
		return access_token;
	}
	public String auth2_login(String checkUrl,String redirect,String scope,String[]... params) throws Exception{
		Cache cache = Redis.use();
		String state = DateUtil.format(new Date(), "HHmmss")+StringUtil.getRandomString(30);
		cache.hset(Constant.R_PRE+"WEIXINSDK:STATE:"+state,"redirect", redirect);
		cache.hset(Constant.R_PRE+"WEIXINSDK:STATE:"+state,"scope", scope);
		//某些项目可能需要，兼容下
		if(params!=null&&params.length>0){
			for(String[] item:params){
				cache.hset(Constant.R_PRE+"WEIXINSDK:STATE:"+state,item[0], item[1]);
			}
		}
		cache.expire(Constant.R_PRE+"WEIXINSDK:STATE:"+state, 3600);
		StringBuilder str = new StringBuilder("https://open.weixin.qq.com/connect/oauth2/authorize");
		str.append("?appid="+appid);
		str.append("&redirect_uri="+URLEncoder.encode(checkUrl,"UTF-8"));
		str.append("&response_type=code");
		str.append("&scope="+scope);
		str.append("&state="+state+"#wechat_redirect");
		return str.toString();
	}
	/***
	 * 获取auth2 AccessToken
	 * @param code
	 * @return
	 */
	public Ret auth2_access_token(String code){
		String result = "";
		try{
			Map<String,String> queryParas = new HashMap<String,String>();
			queryParas.put("appid", appid);
			queryParas.put("secret", secret);
			queryParas.put("code", code);
			queryParas.put("grant_type", "authorization_code");
			result = HttpKit.get("https://api.weixin.qq.com/sns/oauth2/access_token", queryParas);
		}catch (Exception e){
			e.printStackTrace();
			return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE, "与公众号通讯失败");
		}
		JSONObject json = JSONObject.parseObject(result);
		if(json.get(errcode)==null||json.getInteger(errcode)==0){
			Ret ret = new Ret();
			ret.set(Constant.RESULT, Constant.SUCCESS);
			ret.set("openid", json.getString("openid"));
			ret.set("scope", json.getString("scope"));
			ret.set("access_token", json.getString("access_token"));
			ret.set("refresh_token", json.getString("refresh_token"));
			return ret;
		}
		return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE,json.getInteger("errcode")+":"+json.getString("errmsg"));
	}
	/***
	 * auth2获取用户基本信息
	 * @param access_token
	 * @param openid
	 * @return
	 */
	public Ret auth2_userinfo(String openid,String access_token){
		String result = "";
		try{
			Map<String,String> queryParas = new HashMap<String,String>();
			queryParas.put("access_token", access_token);
			queryParas.put("openid", openid);
			queryParas.put("lang", "zh_CN");
			result = HttpKit.get("https://api.weixin.qq.com/sns/userinfo", queryParas);
		}catch (Exception e){
			e.printStackTrace();
			return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE, "与公众号通讯失败");
		}
		JSONObject json = JSONObject.parseObject(result);
		if(json.get(errcode)==null||json.getInteger(errcode)==0){
			Ret ret = new Ret();
			ret.set(Constant.RESULT, Constant.SUCCESS);
			ret.set("nickname", StringUtil.filterOffUtf8Mb4(json.getString("nickname")));
			ret.set("sex", json.getString("sex"));
			ret.set("province", json.getString("province"));
			ret.set("city", json.getString("city"));
			ret.set("country", json.getString("country"));
			ret.set("headimgurl", json.getString("headimgurl"));
			if(json.get("unionid")!=null){
				ret.set("unionid", json.getString("unionid"));
			}
			return ret;
		}
		return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE,json.getInteger("errcode")+":"+json.getString("errmsg"));
	}
	
	/***
	 * 批量获取素材
	 * @param type
	 * @param offset
	 * @param count
	 * @return
	 */
	public JSONObject material_batchget(String type,Integer offset,Integer count){
		String result = "";
		try{
			JSONObject data = new JSONObject();
			data.put("type", type);
			data.put("offset", offset);
			data.put("count", count);
			result = HttpKit.post("https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token="+access_token(), data.toJSONString());
		}catch (Exception e){
			e.printStackTrace();
			return null;
		}
		JSONObject json = JSONObject.parseObject(result);
		if(json.get(errcode)==null||json.getInteger(errcode)==0){
			return json;
		}
		return null;
	}
	/***
	 * 删除素材
	 * @param media_id
	 * @return
	 */
	public Ret material_del(String media_id){
		String result = "";
		try{
			JSONObject data = new JSONObject();
			data.put("media_id", media_id);
			result = HttpKit.post("https://api.weixin.qq.com/cgi-bin/material/del_material?access_token="+access_token(), data.toJSONString());
		}catch (Exception e){
			e.printStackTrace();
			return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE, "与公众号通讯失败");
		}
		JSONObject json = JSONObject.parseObject(result);
		if(json.get(errcode)==null||json.getInteger(errcode)==0){
			return Ret.create(Constant.RESULT, Constant.SUCCESS);
		}
		return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE,json.getInteger("errcode")+":"+json.getString("errmsg"));
	}
	/***
	 * 获取图文素材
	 * @param media_id
	 * @return
	 */
	public Ret material_get_news(String media_id){
		String result = "";
		try{
			JSONObject data = new JSONObject();
			data.put("media_id", media_id);
			result = HttpKit.post("https://api.weixin.qq.com/cgi-bin/material/get_material?access_token="+access_token(), data.toJSONString());
		}catch (Exception e){
			e.printStackTrace();
			return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE, "与公众号通讯失败");
		}
		JSONObject json = JSONObject.parseObject(result);
		if(json.get(errcode)==null||json.getInteger(errcode)==0){
			return Ret.create(Constant.RESULT, Constant.SUCCESS).set("news_item", json.getJSONArray("news_item"));
		}
		return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE,json.getInteger("errcode")+":"+json.getString("errmsg"));
	}
	
	public String media_get_file(String media_id){
		String url = "https://api.weixin.qq.com/cgi-bin/media/get?"
				+ "access_token="+access_token()+"&media_id="+media_id;
		Ret ret = UploadTool.downloadFile(url, "weixin", 0);
		if(ret.getStr(Constant.RESULT).equals(Constant.SUCCESS)){
			return ret.getStr("filePath");
		}
		return null;
	}
	
	/***
	 * 创建默认菜单
	 * @param menu
	 * @return
	 */
	public Ret menu_create(JSONObject menu){
		String result = "";
		try{
			result = HttpKit.post("https://api.weixin.qq.com/cgi-bin/menu/create?access_token="
					+access_token(), menu.toJSONString());
		}catch (Exception e) {
			e.printStackTrace();
			return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE, "与公众号通讯失败");
		}
		JSONObject json = JSONObject.parseObject(result);
		if(json.get(errcode)==null||json.getInteger(errcode)==0){
			return Ret.create(Constant.RESULT, Constant.SUCCESS);
		}
		return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE,json.getInteger("errcode")+":"+json.getString("errmsg"));
	}
	/***
	 * 默认菜单删除
	 * @return
	 */
	public Ret menu_delete(){
		String result = "";
		try{
			result = HttpKit.get("https://api.weixin.qq.com/cgi-bin/menu/delete?access_token="+access_token());
		}catch (Exception e) {
			e.printStackTrace();
			return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE, "与公众号通讯失败");
		}
		JSONObject json = JSONObject.parseObject(result);
		if(json.get(errcode)==null||json.getInteger(errcode)==0){
			return Ret.create(Constant.RESULT, Constant.SUCCESS).set("menuid", json.getString("menuid"));
		}
		return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE,json.getInteger("errcode")+":"+json.getString("errmsg"));
	}
	/***
	 * 创建个性化菜单
	 * @param menu
	 * @return
	 */
	public Ret menu_addconditional(JSONObject menu){
		String result = "";
		try{
			result = HttpKit.post("https://api.weixin.qq.com/cgi-bin/menu/addconditional?access_token="
					+access_token(), menu.toJSONString());
		}catch (Exception e) {
			e.printStackTrace();
			return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE, "与公众号通讯失败");
		}
		JSONObject json = JSONObject.parseObject(result);
		if(json.get(errcode)==null||json.getInteger(errcode)==0){
			return Ret.create(Constant.RESULT, Constant.SUCCESS).set("menuid", json.getString("menuid"));
		}
		return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE,json.getInteger("errcode")+":"+json.getString("errmsg"));
	}
	/***
	 * 删除个性化菜单
	 * @param menuid
	 * @return
	 */
	public Ret menu_delconditional(String menuid){
		String result = "";
		JSONObject data = new JSONObject();
		data.put("menuid", menuid);
		try{
			result = HttpKit.post("https://api.weixin.qq.com/cgi-bin/menu/delconditional?access_token="
					+access_token(), data.toJSONString());
		}catch (Exception e) {
			e.printStackTrace();
			return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE, "删除个性化菜单与公众号通讯失败");
		}
		JSONObject json = JSONObject.parseObject(result);
		if(json.get(errcode)==null||json.getInteger(errcode)==0){
			return Ret.create(Constant.RESULT, Constant.SUCCESS);
		}
		return Ret.create(Constant.RESULT, Constant.ERROR).set("删除个性化菜单出错["+Constant.MESSAGE,json.getInteger("errcode")+":"+json.getString("errmsg")+"]");
	}
	/***
	 * 创建二维码
	 * @return
	 */
	public Ret qrcode_create(String action_name,Integer scene_id,long expire_seconds){
		String result = "";
		JSONObject data = new JSONObject();
		data.put("action_name", action_name);
		JSONObject action_info = new JSONObject();
		JSONObject scene = new JSONObject();
		scene.put("scene_id", scene_id);
		action_info.put("scene", scene);
		data.put("action_info", action_info);
		if(expire_seconds>0){
			data.put("expire_seconds", expire_seconds);
		}
		try{
			result = HttpKit.post("https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token="
					+access_token(), data.toJSONString());
		}catch (Exception e) {
			e.printStackTrace();
			return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE, "与公众号通讯失败");
		}
		JSONObject json = JSONObject.parseObject(result);
		if(json.get(errcode)==null||json.getInteger(errcode)==0){
			return Ret.create(Constant.RESULT, Constant.SUCCESS).set("ticket", json.getString("ticket")).set("url", json.getString("url"));
		}
		return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE,json.getInteger("errcode")+":"+json.getString("errmsg"));
	}
	/***
	 * 发送客服消息
	 * @param openid
	 * @param type
	 * @param content
	 * @return
	 */
	public Ret custom_send(String openid,String type,String content){
		String result = "";
		JSONObject data = new JSONObject();
		data.put("touser", openid);
		data.put("msgtype", type);
		JSONObject info = new JSONObject();
		switch (type) {
			case "text":
				info.put("content", content);
				data.put("text", info);
				break;
			case "image":
				info.put("media_id", content);
				data.put("image", info);
				break;
			case "mpnews":
				info.put("media_id", content);
				data.put("mpnews", info);
				break;
			default:
				return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE,"不可用的消息类型");
		}
		try{
			result = HttpKit.post("https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token="
					+access_token(), data.toJSONString());
		}catch (Exception e) {
			e.printStackTrace();
			return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE, "与公众号通讯失败");
		}
		JSONObject json = JSONObject.parseObject(result);
		if(json.get(errcode)==null||json.getInteger(errcode)==0){
			return Ret.create(Constant.RESULT, Constant.SUCCESS);
		}
		return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE,json.getInteger("errcode")+":"+json.getString("errmsg"));
	}

	/***
	 * 签名检查
	 * @param signature
	 * @param timestamp
	 * @param nonce
	 * @return
	 */
	public boolean checkSignature(String signature,String timestamp,String nonce){
		String[] array = new String[] { token, timestamp, nonce };
		StringBuffer sb = new StringBuffer();
		Arrays.sort(array);
		for (int i = 0; i < 3; i++) {
            sb.append(array[i]);
        }
		if(HashKit.sha1(sb.toString()).equals(signature)){
			return true;
		}else {
			return false;
		}
	}
	/***
	 * 获取Jsapiticket
	 * @return
	 */
	private static final Object ticket_lock = new Object();
	public String ticket_get(){
		Cache cache = Redis.use();
		if(cache.exists(Constant.R_PRE+"WEIXINSDK:JSAPITICKET:"+appid)){
			return cache.get(Constant.R_PRE+"WEIXINSDK:JSAPITICKET:"+appid);
		}
		synchronized (ticket_lock) {
			if(cache.exists(Constant.R_PRE+"WEIXINSDK:JSAPITICKET:"+appid)){
				return cache.get(Constant.R_PRE+"WEIXINSDK:JSAPITICKET:"+appid);
			}
			String result = "";
			try{
				result = HttpKit.get("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="
						+access_token()+"&type=jsapi");
			}catch(Exception e){
				e.printStackTrace();
				return "";
			}
			JSONObject json = JSONObject.parseObject(result);
			if(json.get("errcode")==null||json.getInteger("errcode")==0){
				String jsapiTicket = json.getString("ticket");
				cache.setex(Constant.R_PRE+"WEIXINSDK:JSAPITICKET:"+appid,7000, jsapiTicket);
				return jsapiTicket;
			}
		}
		return null;
	}
	public Ret getJsConfig(String url){
		long timestamp = DateUtil.date().getTime()/1000;
		String noncestr = StringUtil.getRandomString(50);
		String sign = HashKit.sha1("jsapi_ticket=" + ticket_get() 
				+ "&noncestr=" + noncestr
		        + "&timestamp=" + timestamp 
		        + "&url=" + url);
		return Ret.create("noncestr", noncestr).set("timestamp",timestamp).set("sign", sign).set("appid", appid);
	}
	
	//{"touser":"","template_id":"","data":{"keyword1":{"value":"","color":""},"keyword2":{"value":"","color":""}}}
	//{"keyword1":{"value":"","color":""},"keyword2":{"value":"","color":""}}
	public Ret message_template_send(String touser,String template_id,String url,JSONObject data){
		JSONObject jsonObject = new JSONObject();
		jsonObject.put("touser", touser);
		jsonObject.put("template_id", template_id);
		jsonObject.put("url", url);
		jsonObject.put("data", data);
		String result = "";
		try{
			result = HttpKit.post("https://api.weixin.qq.com/cgi-bin/message/template/send?"
					+ "access_token="+access_token(), jsonObject.toJSONString());
		}catch (Exception e) {
			e.printStackTrace();
		}
		Ret ret = new Ret();
		JSONObject resultObject = JSONObject.parseObject(result);
        if(resultObject.getInteger("errcode").equals(0)){
			ret.set(Constant.RESULT, Constant.SUCCESS);
        }else {
			ret.set(Constant.RESULT, Constant.ERROR);
			ret.set(Constant.MESSAGE, resultObject.getString("errmsg"));
        }
        return ret;
	}
	/***
	 * 获取用户基本信息(只能已关注的)
	 */
	public Ret user_info(String openid){
		String result = "";
		try{
			result = HttpKit.get("https://api.weixin.qq.com/cgi-bin/user/info?access_token="
					+access_token()+"&openid="+openid+"&lang=zh_CN");
		}catch (Exception e) {
			e.printStackTrace();
			return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE, "接口调用失败！");
		}
		JSONObject jsonObject = JSONObject.parseObject(result);
		if(jsonObject.get("errcode")!=null&&jsonObject.getInteger("errcode")>0){
			return Ret.create(Constant.RESULT, Constant.ERROR).set(Constant.MESSAGE, jsonObject.getInteger("errcode")+":"+jsonObject.getString("errmsg"));
		}
		Ret ret = Ret.create(Constant.RESULT, Constant.SUCCESS)
				.set("subscribe", jsonObject.getInteger("subscribe"));
		if(jsonObject.getInteger("subscribe")>0){
			ret.set("openid", jsonObject.getString("openid"));
			ret.set("nickname", jsonObject.getString("nickname"));
			if(jsonObject.get("sex")!=null){
				ret.set("sex", jsonObject.getInteger("sex"));
			}
			if(jsonObject.get("city")!=null){
				ret.set("city", jsonObject.getString("city"));
			}
			if(jsonObject.get("province")!=null){
				ret.set("province", jsonObject.getString("province"));
			}
			if(jsonObject.get("country")!=null){
				ret.set("country", jsonObject.getString("country"));
			}
			if(jsonObject.get("headimgurl")!=null){
				ret.set("headimgurl", jsonObject.getString("headimgurl"));
			}
			ret.set("subscribe_time", jsonObject.getString("subscribe_time"));
		}
		return ret;
	}
	public String getAppid() {
		return appid;
	}
	public void setAppid(String appid) {
		this.appid = appid;
	}
	public String getSecret() {
		return secret;
	}
	public void setSecret(String secret) {
		this.secret = secret;
	}
	public String getToken() {
		return token;
	}
	public void setToken(String token) {
		this.token = token;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
